import { IFunction, IObject } from "@/types/interface";
import { debounce, DebouncedFunc, DebounceSettings } from "lodash";
import type { App, Plugin } from "vue";

/**
 * 获取对象下的字段值
 * @param record {}
 * @param key 'a.b.c'
 * @param defaultValue
 * @returns
 */
export const getValueByKeys = (record: IObject = {}, key: string, defaultValue?: unknown): any => {
  const keys = key.split(".");
  for (let i = 0; i < keys.length; i++) {
    record = record[keys[i]] || (i === keys.length - 1 ? defaultValue : {});
  }
  return record || defaultValue;
};

/**
 * 数组转对象
 * @param {*} rs 集合
 * @param {*} key 需要转换目标key的名称
 */
export const arrayToObject = (
  rs: any[] = [],
  key: string | IFunction,
  render?: IFunction
): IObject => {
  const o: IObject = {};
  rs.forEach((x) => {
    o[typeof key === "function" ? key(x) : x[key]] = render ? render(x) : x;
  });
  return o;
};

/**
 * 数组转换格式
 * @param rs
 * @param render
 * @returns
 */
export const arrayToKeyValueArray = (rs: any[] = [], render?: IFunction): any[] => {
  return rs.map((x) => (render ? render(x) : typeof x === "object" ? x : { label: x, value: x }));
};

/**
 * 对象转为数组
 * @param: {Object} obj-- eg:{1:{name:1,id:1,tagType:'info'}}
 * @returns: {Array}
 */
export const objToArray = (obj: {
  [value: string | number]: { name: string; id: string | number };
}): Array<{
  name: string;
  id: string | number;
}> => {
  return Object.values(obj)?.map((item) => {
    return {
      name: item.name,
      id: item.id
    };
  });
};

/**
 * 是否只null和undefined值
 * @param vl
 * @returns
 */
export const isNullOrUndefined = (vl: unknown): boolean => {
  return vl === null || typeof vl === "undefined";
};

/**
 * 是否外链
 * @param path
 * @returns
 */
export const isExternalLink = (path: string): boolean => {
  return /^(https?:|\/\/|mailto:|tel:|^{{\s?ApiUrl\s?}})/.test(path);
};

/**
 * 复制
 * @param value
 */
export const copyToClipboard = (value: string): void => {
  const transfer = document.createElement("textarea");
  document.body.appendChild(transfer);
  transfer.value = value;
  transfer.focus();
  transfer.select();
  if (document.execCommand("copy")) {
    document.execCommand("copy");
  }
  transfer.blur();
  document.body.removeChild(transfer);
};

/**
 * 检查是否有权限
 * @param permission
 * @param key
 * @returns
 */
export const checkPermission = (permission: string[], key: string): boolean => {
  return permission.includes(key);
};

/**
 * 获取uuid
 */
export const getUuid = (): string => {
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
    const r = (Math.random() * 16) | 0,
      v = c == "x" ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
};

/**
 * 邮箱
 * @param {*} s
 */
export const isEmail = (s: string): boolean => {
  return /^([a-zA-Z0-9._-])+@([a-zA-Z0-9_-])+((.[a-zA-Z0-9_-]{2,3}){1,2})$/.test(s);
};

/**
 * 手机号码
 * @param {*} s
 */
export const isMobile = (s: string): boolean => {
  return /^1[0-9]{10}$/.test(s);
};

/**
 * 电话号码
 * @param {*} s
 */
export const isPhone = (s: string): boolean => {
  return /^([0-9]{3,4}-)?[0-9]{7,8}$/.test(s);
};

/**
 * 身份证号
 * @param {*} s
 */
export const validateIdCard = (
  num: string
): {
  isValid: boolean;
  msg: string;
} => {
  num = num.toUpperCase(); // 身份证为末尾可能是X
  //   身份证号码为15位或者18位，15位时全为数字，18位前17位为数字，
  //   最后一位是校验位，可能为数字或字符X。
  const arrInt = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  const arrCh = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"];
  let nTemp = 0,
    i;
  if (!/(^\d{15}$)|(^\d{17}([0-9]|X)$)/.test(num)) {
    //  输入的身份证号长度不对，或者号码不符合规定;
    return {
      isValid: false,
      msg: "输入的身份证号长度不对，或者号码不符合规定"
    };
  }
  //   校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
  //   下面分别分析出生日期和校验位
  const len = num.length;
  if (len == 15) {
    const reg = new RegExp(/^(\d{6})(\d{2})(\d{2})(\d{2})(\d{3})$/);
    const arrSplit = num.match(reg) || [];
    //   检查生日日期是否正确
    const dtmBirth = new Date(`19${arrSplit[2]}/${arrSplit[3]}/${arrSplit[4]}`);
    const bGoodDay =
      dtmBirth.getFullYear() == Number(arrSplit[2]) &&
      dtmBirth.getMonth() + 1 == Number(arrSplit[3]) &&
      dtmBirth.getDate() == Number(arrSplit[4]);
    if (!bGoodDay) {
      //   输入的身份证号里出生日期不对！
      return {
        isValid: false,
        msg: "输入的身份证号里出生日期不对！"
      };
    } else {
      //   将15位身份证转成18位--soda
      //   校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
      num = `${num.slice(0, 6)}19${num.slice(6)}`;
      for (i = 0; i < 17; i++) {
        nTemp += Number(num[i]) * arrInt[i];
      }
      num += arrCh[nTemp % 11];
      return {
        isValid: true,
        msg: num
      };
    }
  } else if (len == 18) {
    const reg = new RegExp(/^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$/);
    const arrSplit = num.match(reg) || [];
    //  检查生日日期是否正确
    const dtmBirth = new Date(`${arrSplit[2]}/${arrSplit[3]}/${arrSplit[4]}`);
    const bGoodDay =
      dtmBirth.getFullYear() == Number(arrSplit[2]) &&
      dtmBirth.getMonth() + 1 == Number(arrSplit[3]) &&
      dtmBirth.getDate() == Number(arrSplit[4]);
    if (!bGoodDay) {
      //  输入的身份证号里出生日期不对！
      return {
        isValid: false,
        msg: "输入的身份证号里出生日期不对"
      };
    } else {
      //  检验18位身份证的校验码是否正确。
      //  校验位按照ISO 7064:1983.MOD 11-2的规定生成，X可以认为是数字10。
      for (i = 0; i < 17; i++) {
        nTemp += Number(num[i]) * arrInt[i];
      }
      const valnum = arrCh[nTemp % 11];
      if (valnum != num[17]) {
        //  18位身份证的校验码不正确！
        return {
          isValid: false,
          msg: "身份证的校验码不正确！"
        };
      }
      return {
        isValid: true,
        msg: "验证成功"
      };
    }
  }
  return {
    isValid: false,
    msg: "身份证的长度不正确！"
  };
};

/**
 * 正整数
 * @param {*} s
 */
export const isNumber = (s: string): boolean => {
  return /^\+?[1-9][0-9]*$/.test(s);
};

/**
 * 获取字典数据列表
 * @param dictType  字典类型
 */
export const getDictDataList = (list: IObject[], dictType?: string): IObject[] => {
  const type = list.find((element: IObject) => element.dictType === dictType);
  if (type) {
    return type.dataList;
  } else {
    return [];
  }
};

/**
 * 获取字典名称
 * @param dictType  字典类型
 * @param dictValue  字典值
 */
export const getDictLabel = (
  list: IObject[],
  dictType: string,
  dictValue: number
): string | number => {
  const type = list.find((element: IObject) => element.dictType === dictType);
  if (type) {
    const val = type.dataList.find((element: IObject) => element.dictValue === dictValue + "");
    if (val) {
      return val.dictLabel;
    } else {
      return dictValue;
    }
  } else {
    return dictValue;
  }
};

/**
 * 获取svg图标(id)列表
 */
export const getIconList = (): string[] => {
  const rs: string[] = [];
  const list = document.querySelectorAll("svg symbol");
  for (let i = 0; i < list.length; i++) {
    rs.push(list[i].id);
  }
  return rs;
};

/**
 * 树形数据转换
 * @param {*} data
 * @param {*} id
 * @param {*} pid
 */
export const treeDataTranslate = (data: IObject[], id?: string, pid?: string): IObject[] => {
  const res: IObject[] = [];
  const temp: IObject = {};
  id = id || "id";
  pid = pid || "pid";
  for (let i = 0; i < data.length; i++) {
    temp[data[i][id]] = data[i];
  }
  for (let k = 0; k < data.length; k++) {
    if (!temp[data[k][pid]] || data[k][id] === data[k][pid]) {
      res.push(data[k]);
      continue;
    }
    if (!temp[data[k][pid]]["children"]) {
      temp[data[k][pid]]["children"] = [];
    }
    temp[data[k][pid]]["children"].push(data[k]);
    data[k]["_level"] = (temp[data[k][pid]]._level || 0) + 1;
  }
  return res;
};

/**
 * 全局组件安装
 * @param component
 * @param alias
 * @returns
 */
export const withInstall = <T extends object>(component: T, alias?: string): T & Plugin => {
  const comp = component as any;
  comp.install = (app: App) => {
    app.component(comp.name || comp.displayName, component);
    if (alias) {
      app.config.globalProperties[alias] = component;
    }
  };
  return component as T & Plugin;
};

/**
 * 节流函数 `created() {this.dataFormSubmitHandle = useDebounce(this.dataFormSubmitHandle);}`
 * @param fn
 * @param wait
 * @returns
 */
export const useDebounce = (
  fn: (e?: any) => any,
  wait?: number,
  options?: DebounceSettings
): DebouncedFunc<any> => {
  return debounce(fn, wait ?? 1000, {
    leading: true,
    trailing: false,
    ...options
  });
};

// treeselect 数据结构转化
export const transformData = (data: any) => {
  return data.map((item: any) => ({
    value: item.id,
    label: item.name,
    children: item.cities ? transformCities(item.cities) : null
  }));
};

const transformCities = (cities: any) => {
  return cities.map((city: any) => ({
    value: city.id,
    label: city.name,
    children: city.counties ? transformCounties(city.counties) : null
  }));
};

const transformCounties = (counties: any) => {
  return counties.map((county: any) => ({
    value: county.id,
    label: county.name,
    children: county.counties ? transformCounties(county.counties) : null
  }));
};

// 取前几层的数据
export const getNestedStructure = (
  data: any,
  levels: any,
  { keyName, valueName } = {
    keyName: "label",
    valueName: "value"
  }
) => {
  if (levels === 0) {
    return [];
  }

  return data.map((item: any) => {
    const newItem: any = {
      label: item[keyName],
      value: item[valueName]
    };

    if (item.children && levels > 1) {
      newItem.children = getNestedStructure(item.children, levels - 1, { keyName, valueName });
    }

    return newItem;
  });
};

export const fileHandle = (data: any, fileName: string, type: string) => {
  const blob = new Blob([data], {
    type:
      type === "all"
        ? "application/zip"
        : type === "xlsx"
        ? "application/vnd.ms-excel"
        : "application/msword"
  });
  const url = window.URL.createObjectURL(blob);
  const link = document.createElement("a"); // 创建a标签
  link.href = url;
  link.download = fileName; // 重命名文件
  link.click();
  URL.revokeObjectURL(url); // 释放内存
};

export const findObjectByPid: any = (data: any, pid: any, result = []) => {
  for (let obj of data) {
      if (obj.pid === pid) {
          result.push(obj);
      }
      if (obj.children) {
        findObjectByPid(obj.children, pid, result);
      }
  }
  return result;
}

export const validateMobile = (rule: any, value: string, callback: (error?: Error) => void) => {
  console.log(value, '')
  if (value === '') {
    callback(new Error('请输入手机号'));
  } else if (!/^1(3|4|5|6|7|8|9)[0-9]\d{8}$/.test(value)) {
    callback(new Error('请输入正确的手机号'));
  } else {
    callback();
  }
};